﻿// <copyright file="CharacterImporter.cs" company="Gravity Age Studios">
// Code released under the MIT license (see License.txt).
// </copyright>
// <author>Vicente Cartas Espinel</author>
// <email>vicente.cartas@gmail.com</email>
namespace LightSwitchApplication.Import
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;

    internal class CharacterImporter : VisualCollectionImporter<Character> // TODO: ADD OWNER PLAYER!!!
    {
        private readonly int State = 0;
        private readonly int Visibility = 1;
        private readonly int Name = 2;
        private readonly int Gender = 3;
        private readonly int Race = 4;
        private readonly int Alignment = 5;
        private readonly int Faith = 6;
        private readonly int BloodlineDerivation = 7;
        private readonly int BloodlineStrength = 8;
        private readonly int BloodlineScore = 9;
        private readonly int RPSaved = 10;
        private readonly int BloodAbilities = 11;
        private readonly int Classes = 12;
        private readonly int Skills = 13;
        private readonly int LieutenantOf = 14;
        private readonly int PublicInformation = 15;
        private readonly int OwnerInformation = 16;
        private readonly int GMInformation = 17;

        private IEnumerable<Race> races;
        private IEnumerable<Alignment> alignments;
        private IEnumerable<Deity> deities;
        private IEnumerable<BloodlineDerivation> derivations;
        private IEnumerable<BloodlineStrength> strengths;
        private IEnumerable<BloodAbility> abilities;
        private IEnumerable<CharacterClass> classes;
        private IEnumerable<Skill> skills;

        public CharacterImporter(DataWorkspace workspace)
            : base(workspace)
        {
        }

        protected override void FetchRequisites()
        {
            this.races = this.workspace.ApplicationData.Races.GetQuery().Execute();
            this.alignments = this.workspace.ApplicationData.Alignments.GetQuery().Execute();
            this.deities = this.workspace.ApplicationData.Deities.GetQuery().Execute();
            this.derivations = this.workspace.ApplicationData.BloodlineDerivations.GetQuery().Execute();
            this.strengths = this.workspace.ApplicationData.BloodlineStrengths.GetQuery().Execute();
            this.abilities = this.workspace.ApplicationData.BloodAbilities.GetQuery().Execute();
            this.classes = this.workspace.ApplicationData.CharacterClasses.GetQuery().Execute();
            this.skills = this.workspace.ApplicationData.Skills.GetQuery().Execute();
        }

        protected override bool SkipEntity(int index)
        {
            if (this.headers[Name].HasValue)
            {
                if (this.entities.Any(e => e.Name.Equals(data[index + headers[Name].Value].Trim(), StringComparison.InvariantCultureIgnoreCase)))
                {
                    return true;
                }
            }

            return false;
        }

        protected override int?[] MapHeaders(string[] stringHeaders)
        {
            int?[] headers = new int?[18];

            for (int i = 0; i < stringHeaders.Length; i++)
            {
                if (stringHeaders[i].StartsWith("Sta", StringComparison.InvariantCultureIgnoreCase))
                {
                    headers[State] = i;
                }
                else if (stringHeaders[i].StartsWith("Vis", StringComparison.InvariantCultureIgnoreCase))
                {
                    headers[Visibility] = i;
                }
                else if (stringHeaders[i].StartsWith("Name", StringComparison.InvariantCultureIgnoreCase))
                {
                    headers[Name] = i;
                }
                else if (stringHeaders[i].StartsWith("Gen", StringComparison.InvariantCultureIgnoreCase))
                {
                    headers[Gender] = i;
                }
                else if (stringHeaders[i].StartsWith("Rac", StringComparison.InvariantCultureIgnoreCase))
                {
                    headers[Race] = i;
                }
                else if (stringHeaders[i].StartsWith("Al", StringComparison.InvariantCultureIgnoreCase))
                {
                    headers[Alignment] = i;
                }
                else if (stringHeaders[i].StartsWith("Fai", StringComparison.InvariantCultureIgnoreCase))
                {
                    headers[Faith] = i;
                }
                else if (stringHeaders[i].Contains("Deriv") || stringHeaders[i].Contains("deriv"))
                {
                    headers[BloodlineDerivation] = i;
                }
                else if (stringHeaders[i].Contains("Stren") || stringHeaders[i].Contains("stren"))
                {
                    headers[BloodlineStrength] = i;
                }
                else if (stringHeaders[i].Contains("Scor") || stringHeaders[i].Contains("scor"))
                {
                    headers[BloodlineScore] = i;
                }
                else if (stringHeaders[i].StartsWith("RP", StringComparison.InvariantCultureIgnoreCase))
                {
                    headers[RPSaved] = i;
                }
                else if (stringHeaders[i].Contains("Abil") | stringHeaders[i].Contains("abil"))
                {
                    headers[BloodAbilities] = i;
                }
                else if (stringHeaders[i].StartsWith("Clas", StringComparison.InvariantCultureIgnoreCase))
                {
                    headers[Classes] = i;
                }
                else if (stringHeaders[i].StartsWith("Skil", StringComparison.InvariantCultureIgnoreCase))
                {
                    headers[Skills] = i;
                }
                else if (stringHeaders[i].StartsWith("Lie", StringComparison.InvariantCultureIgnoreCase))
                {
                    headers[LieutenantOf] = i;
                }
                else if (stringHeaders[i].StartsWith("Public", StringComparison.InvariantCultureIgnoreCase))
                {
                    headers[PublicInformation] = i;
                }
                else if (stringHeaders[i].StartsWith("Owner", StringComparison.InvariantCultureIgnoreCase))
                {
                    headers[OwnerInformation] = i;
                }
                else if (stringHeaders[i].StartsWith("GM", StringComparison.InvariantCultureIgnoreCase))
                {
                    headers[GMInformation] = i;
                }
            }

            return headers;
        }

        protected override void FillEntity(Character entity, int index, int?[] headers, string[] data)
        {
            if (headers[State].HasValue)
            {
                int value;
                if (int.TryParse(data[index + headers[State].Value], out value))
                {
                    entity.State = value;
                }
            }

            if (headers[Visibility].HasValue)
            {
                int value;
                if (int.TryParse(data[index + headers[Visibility].Value], out value))
                {
                    entity.Visibility = value;
                }
            }

            if (headers[Name].HasValue)
            {
                entity.Name = data[index + headers[Name].Value];
            }

            if (headers[Gender].HasValue)
            {
                entity.Gender = data[index + headers[Gender].Value];
            }

            if (headers[Race].HasValue)
            {
                entity.Race = races
                    .Where(e => e.Name.Equals(data[index + headers[Race].Value], StringComparison.InvariantCultureIgnoreCase))
                    .FirstOrDefault();
            }

            if (headers[Alignment].HasValue)
            {
                entity.Alignment = alignments
                    .Where(e => e.Name.Equals(data[index + headers[Alignment].Value], StringComparison.InvariantCultureIgnoreCase))
                    .FirstOrDefault();
            }

            if (headers[Faith].HasValue)
            {
                entity.Faith = deities
                    .Where(e => e.Name.Equals(data[index + headers[Faith].Value], StringComparison.InvariantCultureIgnoreCase))
                    .FirstOrDefault();
            }

            if (headers[BloodlineDerivation].HasValue)
            {
                entity.BloodlineDerivation = derivations
                    .Where(e => e.Name.Equals(data[index + headers[BloodlineDerivation].Value], StringComparison.InvariantCultureIgnoreCase))
                    .FirstOrDefault();
            }

            if (headers[BloodlineStrength].HasValue)
            {
                entity.BloodlineStrength = strengths
                    .Where(e => e.Name.Equals(data[index + headers[BloodlineStrength].Value], StringComparison.InvariantCultureIgnoreCase))
                    .FirstOrDefault();
            }

            if (headers[BloodlineScore].HasValue)
            {
                int value;
                if (int.TryParse(data[index + headers[BloodlineScore].Value], out value))
                {
                    entity.BloodlineScore = value;
                }
            }

            if (headers[RPSaved].HasValue)
            {
                int value;
                if (int.TryParse(data[index + headers[RPSaved].Value], out value))
                {
                    entity.RPSaved = value;
                }
            }

            if (headers[BloodAbilities].HasValue)
            {
                this.ParseBloodAbilities(entity, data[index + headers[BloodAbilities].Value], abilities);
            }

            if (headers[Classes].HasValue)
            {
                this.ParseClasses(entity, data[index + headers[Classes].Value], classes);
            }

            if (headers[Skills].HasValue)
            {
                this.ParseSkills(entity, data[index + headers[Skills].Value], skills);
            }

            // TODO: fix this in a clear way
            if (headers[LieutenantOf].HasValue)
            {
                Character character = null; // collection
                //.FirstOrDefault(e => e.Name.Equals(data[i + headers[LieutenantOf].Value], StringComparison.InvariantCultureIgnoreCase));

                if (character == null)
                {
                    character = entities
                        .FirstOrDefault(e => e.Name.Equals(data[index + headers[LieutenantOf].Value], StringComparison.InvariantCultureIgnoreCase));
                }

                entity.LieutenantOf = character;
            }

            if (headers[PublicInformation].HasValue)
            {
                entity.PublicInformation = data[index + headers[PublicInformation].Value];
            }

            if (headers[OwnerInformation].HasValue)
            {
                entity.OwnerInformation = data[index + headers[OwnerInformation].Value];
            }

            if (headers[GMInformation].HasValue)
            {
                entity.GMInformation = data[index + headers[GMInformation].Value];
            }
        }

        private void ParseBloodAbilities(Character entity, string abilitiesString, IEnumerable<BloodAbility> abilities)
        {
            string[] splitAbilities = abilitiesString.Split(new string[3] { "|", "/", "," }, StringSplitOptions.RemoveEmptyEntries);

            foreach (var ability in splitAbilities)
            {
                var final = ability.Split(new string[1] { "(" }, StringSplitOptions.RemoveEmptyEntries);

                // Get the ability
                var relation = entity.RelationCharacterBloodAbilities.AddNew();
                relation.Character = entity;

                if (final.Length == 1)
                {
                    relation.BloodAbility = abilities.FirstOrDefault(e => e.Name.Equals(final[0].Trim(), StringComparison.InvariantCultureIgnoreCase));
                }
                else
                {
                    relation.BloodAbility = abilities
                        .Where(e => e.Name.Equals(final[0].Trim(), StringComparison.InvariantCultureIgnoreCase))
                        .Where(
                            e =>
                            e.BloodlineStrength.Name.Equals(final[1].Substring(0, final[1].Length - 1).Trim(),
                                                            StringComparison.InvariantCultureIgnoreCase))
                        .FirstOrDefault();
                }
            }
        }

        private void ParseClasses(Character entity, string classesString, IEnumerable<CharacterClass> classes)
        {
            string[] splitClasses = classesString.Split(new string[3] { "|", "/", "," }, StringSplitOptions.RemoveEmptyEntries);

            foreach (var classLevel in splitClasses)
            {
                int i = 0;
                for (i = 0; i < classLevel.Length; i++)
                {
                    if (char.IsNumber(classLevel[i]))
                    {
                        break;
                    }
                }

                // Get the class
                var relation = entity.RelationCharacterClasses.AddNew();
                relation.Character = entity;
                relation.CharacterClass = classes.FirstOrDefault(e => e.Abbreviation.Equals(classLevel.Substring(0, i), StringComparison.InvariantCultureIgnoreCase));

                // Get the level
                int level;
                if (int.TryParse(classLevel.Substring(i, classLevel.Length - i), out level))
                {
                    relation.Level = level;
                }
            }
        }

        private void ParseSkills(Character entity, string skillsString, IEnumerable<Skill> skills)
        {
            string[] splitSkills = skillsString.Split(new string[3] { "|", "/", "," }, StringSplitOptions.RemoveEmptyEntries);

            foreach (var skillLevel in splitSkills)
            {
                var final = skillLevel.Split(new string[1] { " " }, StringSplitOptions.RemoveEmptyEntries);

                // Get the skill
                var relation = entity.RelationCharacterSkills.AddNew();
                relation.Character = entity;
                relation.Skill = skills.FirstOrDefault(e => e.Name.Equals(final[0].Trim(), StringComparison.InvariantCultureIgnoreCase));

                // Get the level
                int level;
                if (int.TryParse(final[1].Trim(), out level))
                {
                    relation.Level = level;
                }
            }
        }
    }
}
